/*
 * Copyright 2002-2010 Edward L. Stauff.                  Contact: <EdStauff@gmail.com>.
 * 
 * This file, "NiceRegistryKey.cs", is part of the "Stauffware.Common" class library.
 * "Stauffware.Common" is free software: you can redistribute it and/or 
 * modify it under the terms of the GNU Lesser Public License as published by the 
 * Free Software Foundation, either version 3 of the License, or (at your option) 
 * any later version.
 * 
 * "Stauffware.Common" is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser Public License for more 
 * details.
 * 
 * You should have received a copy of the GNU Lesser Public License along with 
 * the "Stauffware.Common" sources.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using MSW=Microsoft.Win32;
using Stauffware.Common.Core;

namespace Stauffware.Common.WindowsOS
{
	/// <summary>
	/// An immutable type that represents a Windows registry key.  
	/// Based on and compatible with Microsoft.Win32.RegistryKey MSregKey,
	/// but with better functionality.
	/// </summary>
	public partial class NiceRegistryKey
	{
		#region Top-Level Keys (Hives)

		/// <summary>
		/// Represents the top-level LocalMachine registry key on the current machine.
		/// </summary>
		public static readonly NiceRegistryKey LocalMachine = new NiceRegistryKey(MSW.Registry.LocalMachine);

		/// <summary>
		/// Represents the top-level CurrentUser registry key on the current machine.
		/// </summary>
		public static readonly NiceRegistryKey CurrentUser = new NiceRegistryKey(MSW.Registry.CurrentUser);

		/// <summary>
		/// Represents the top-level Users registry key on the current machine.
		/// </summary>
		public static readonly NiceRegistryKey Users = new NiceRegistryKey(MSW.Registry.Users);

		/// <summary>
		/// Represents the top-level ClassesRoot registry key on the current machine.
		/// </summary>
		public static readonly NiceRegistryKey ClassesRoot = new NiceRegistryKey(MSW.Registry.ClassesRoot);

		/// <summary>
		/// Represents the top-level CurrentConfig registry key on the current machine.
		/// </summary>
		public static readonly NiceRegistryKey CurrentConfig = new NiceRegistryKey(MSW.Registry.CurrentConfig);

		/// <summary>
		/// Represents the top-level PerformanceData registry key on the current machine.
		/// </summary>
		public static readonly NiceRegistryKey PerformanceData = new NiceRegistryKey(MSW.Registry.PerformanceData);

		#endregion

		//=====================================================================
		/// <summary>
		/// Microsoft's registry key object.
		/// </summary>

		public readonly MSW.RegistryKey MSregKey;

		//=====================================================================
		/// <summary>
		/// Gets the fully qualified name of the registry key.
		/// </summary>

		public string FullName
		{
			get
			{
				return this.MSregKey.Name;
			}
		}

		//=====================================================================
		/// <summary>
		/// Gets the name of just this registry key, relative to its parent.
		/// </summary>

		public string ShortName
		{
			get
			{
				if (this._shortName == null)
				{
					int n = this.FullName.LastIndexOf('\\');
					if (n < 0)
						this._shortName = this.FullName;
					else this._shortName = this.FullName.Substring(n + 1);
				}
				return this._shortName;
			}
		}
		private string _shortName;

		//=====================================================================
		/// <summary>
		/// Constructs a NiceRegistryKey from a Microsoft.Win32.RegistryKey.
		/// </summary>

		public NiceRegistryKey (MSW.RegistryKey msRegKey)
		{
			Require.NotNull(msRegKey);
			this.MSregKey = msRegKey;
		}

		//=====================================================================

		#region Static methods
		//=====================================================================
		/// <summary>
		/// Constructs a NiceRegistryKey from a fully qualified registry key path
		/// on the current system.  If the path contains any slashes '/' but no
		/// backslashes '\', then the slashes will be converted to backslashes.
		/// Returns null if the registry key doesn't exist.
		/// </summary>

		public static NiceRegistryKey OpenFromPath (string path)
		{
			string root = NormalizeAndRemoveRoot(ref path);
			NiceRegistryKey key = GetRoot(ParseHive(root));
			if (path != null)
				key = key.OpenSubKey(path);
			return key;
		}

		//=====================================================================
		/// <summary>
		/// Constructs a NiceRegistryKey from a fully qualified registry key path
		/// on the specified system.  If the path contains any slashes '/' but no
		/// backslashes '\', then the slashes will be converted to backslashes.
		/// Returns null if the registry key doesn't exist.
		/// </summary>

		public static NiceRegistryKey OpenFromPath (string system, string path)
		{
			string root = NormalizeAndRemoveRoot(ref path);
			MSW.RegistryKey msKey = 
				MSW.RegistryKey.OpenRemoteBaseKey(ParseHive(root), system);
			if (path != null)
				msKey = msKey.OpenSubKey(path);
			return new NiceRegistryKey(msKey);
		}

		//=====================================================================
		/// <summary>
		/// Constructs a NiceRegistryKey from a fully qualified registry key 
		/// path on the current system, creating the key if it doesn't exist.  
		/// If the path contains any slashes '/' but no backslashes '\', 
		/// then the slashes will be converted to backslashes.
		/// </summary>

		public static NiceRegistryKey CreateFromPath (string path)
		{
			string root = NormalizeAndRemoveRoot(ref path);
			NiceRegistryKey key = GetRoot(ParseHive(root));
			if (path != null)
				key = key.CreateSubKey(path);
			return key;
		}

		//=====================================================================
		/// <summary>
		/// Constructs a NiceRegistryKey from a fully qualified registry key 
		/// path on the specified system, creating the key if it doesn't exist.  
		/// If the path contains any slashes '/' but no backslashes '\', 
		/// then the slashes will be converted to backslashes.
		/// </summary>

		public static NiceRegistryKey CreateFromPath (string system, string path)
		{
			string root = NormalizeAndRemoveRoot(ref path);
			MSW.RegistryKey msKey = MSW.RegistryKey.OpenRemoteBaseKey(ParseHive(root), system);
			if (path != null)
				msKey = msKey.CreateSubKey(path);
			return new NiceRegistryKey(msKey);
		}

		//=====================================================================
		/// <summary>
		/// Constructs a NiceRegistryKey from a fully qualified registry key path
		/// on the current system.  If the path contains any slashes '/' but no
		/// backslashes '\', then the slashes will be converted to backslashes.
		/// </summary>

		[Obsolete("Use OpenFromPath() or CreateFromPath().", false)]
		public static NiceRegistryKey FromPath (string path)
		{
			string root = NormalizeAndRemoveRoot(ref path);
			NiceRegistryKey key = GetRoot(ParseHive(root));
			if (path != null)
				key = key.OpenSubKey(path);
			return key;
		}

		//=====================================================================
		/// <summary>
		/// Constructs a NiceRegistryKey from a fully qualified registry key path
		/// on the specified system.  If the path contains any slashes '/' but no
		/// backslashes '\', then the slashes will be converted to backslashes.
		/// </summary>

		[Obsolete("Use OpenFromPath() or CreateFromPath().", false)]
		public static NiceRegistryKey FromPath (string system, string path)
		{
			string root = NormalizeAndRemoveRoot(ref path);
			MSW.RegistryKey msKey = MSW.RegistryKey.OpenRemoteBaseKey(ParseHive(root), system);
			if (path != null)
				msKey = msKey.OpenSubKey(path);
			return new NiceRegistryKey(msKey);
		}

		//=====================================================================
		// Converts a registry key path to its canonical form.
		// Strips off the root (hive) and returns it.

		private static string NormalizeAndRemoveRoot (ref string path)
		{
			if (path.Contains("/") && !path.Contains("\\"))
				path = path.Replace('/', '\\');

			while (path.StartsWith("\\"))
				path = path.Substring(1);

			int n = path.IndexOf('\\');
			string root;
			if (n < 0)
			{
				root = path;
				path = null;
			}
			else
			{
				root = path.Substring(0, n);
				path = path.Substring(n + 1);
			}

			return root;
		}

		//=====================================================================
		// Returns a NiceRegistryKey for the given registry hive.

		private static NiceRegistryKey GetRoot (MSW.RegistryHive hive)
		{
			switch (hive)
			{
				case MSW.RegistryHive.LocalMachine: return LocalMachine;
				case MSW.RegistryHive.CurrentUser: return CurrentUser;
				case MSW.RegistryHive.ClassesRoot: return ClassesRoot;
				case MSW.RegistryHive.CurrentConfig: return CurrentConfig;
				case MSW.RegistryHive.PerformanceData: return PerformanceData;
				case MSW.RegistryHive.Users: return Users;
				default: 
					Assert.NotReached("unknown hive: " + hive.ToString());
					return null;	// shut compiler up
			}
		}

		//=====================================================================
		/// <summary>
		/// Parses a hive name (e.g. "CurrentUser", "LocalMachine") and returns the hive.
		/// Ignores case and underscores.
		/// </summary>

		public static MSW.RegistryHive ParseHive (string name)
		{
			string origName = name;

			name = name.ToLower().Trim().Replace("_", "");

			if (name == "hkeylocalmachine" ||
				name == "hklocalmachine" ||
				name == "localmachine")
				return MSW.RegistryHive.LocalMachine;

			if (name == "hkeycurrentuser" ||
				name == "hkcurrentuser" ||
				name == "currentuser")
				return MSW.RegistryHive.CurrentUser;

			if (name == "hkeyclassesroot" ||
				name == "hkclassesroot" ||
				name == "classesroot")
				return MSW.RegistryHive.ClassesRoot;

			if (name == "hkeycurrentconfig" ||
				name == "hkcurrentconfig" ||
				name == "currentconfig")
				return MSW.RegistryHive.CurrentConfig;

			if (name == "hkeyusers" ||
				name == "hkusers" ||
				name == "users")
				return MSW.RegistryHive.Users;

			if (name == "hkeyperformancedata" ||
				name == "hkperformancedata" ||
				name == "performancedata")
				return MSW.RegistryHive.PerformanceData;

			throw new RegistryException(origName, null, "invalid root key (hive) name: \"" + origName + "\".");
		}

		//=====================================================================
		#endregion

		//=====================================================================
        /// <summary>Creates a new subkey, or opens it if it already exists.</summary>
        /// <param name="nameOrPath">the name or relative path of the subkey to create</param>
        /// <returns>the new registry key</returns>

		public NiceRegistryKey CreateSubKey (string nameOrPath)
		{
			return new NiceRegistryKey(this.MSregKey.CreateSubKey(nameOrPath));
		}

		//=====================================================================
        /// <summary>Opens an existing subkey for read-only access.</summary>
		/// <param name="nameOrPath">the name or relative path of the subkey to open</param>
        /// <returns>the subkey</returns>

		public NiceRegistryKey OpenSubKey (string nameOrPath)
		{
			MSW.RegistryKey msRegKey = this.MSregKey.OpenSubKey(nameOrPath);
			if (msRegKey == null)
				return null;
			return new NiceRegistryKey(msRegKey);
		}

		//=====================================================================
		/// <summary>>Opens an existing subkey.</summary>
		/// <param name="nameOrPath">the name or relative path of the subkey to open</param>
        /// <param name="writable">specifies the access (read-only or read/write)</param>
		/// <returns>the subkey</returns>

		public NiceRegistryKey OpenSubKey (string nameOrPath, bool writable)
		{
			MSW.RegistryKey msRegKey = this.MSregKey.OpenSubKey(nameOrPath, writable);
			if (msRegKey == null)
				return null;
			return new NiceRegistryKey(msRegKey);
		}

		//=====================================================================
        /// <summary>Deletes a value from the registry key.</summary>
		/// <param name="name">the name of the value to delete.</param>
		/// <param name="throwOnMissingValue">whether to throw an exception if the value doesn't exist</param>

		public void DeleteValue(string name, bool throwOnMissingValue)
		{
			this.MSregKey.DeleteValue(name, throwOnMissingValue);
		}

		//=====================================================================
		/// <summary>Deletes a subkey.</summary>
		/// <param name="nameOrPath">the name or relative path of the subkey to delete</param>
		/// <param name="throwOnMissingSubkey">whether to throw an exception if the subkey doesn't exist</param>

		public void DeleteSubKey(string nameOrPath, bool throwOnMissingSubkey)
		{
			this.MSregKey.DeleteSubKey(nameOrPath, throwOnMissingSubkey);
		}

		//=====================================================================
		/// <summary>Deletes a subkey and all of its contents.></summary>
		/// <param name="nameOrPath">the name or relative path of the subkey to delete</param>

		public void DeleteSubKeyTree (string nameOrPath)
		{
			this.MSregKey.DeleteSubKeyTree(nameOrPath);
		}

		//=====================================================================
		/// <summary>
		/// Returns whether this registry key is an ancestor (in the registry
		/// tree) of that registry key.
		/// </summary>

		public bool Contains (NiceRegistryKey that)
		{
			return (this.MSregKey.Name + "\\").StartsWith(that.MSregKey.Name + "\\");
		}

		//=====================================================================
        /// <summary>Returns an array containing the names of this key's subkeys.</summary>
        /// <returns>the subkey names</returns>

		public string[] GetSubKeyNames ()
		{
			return this.MSregKey.GetSubKeyNames();
		}

		//=====================================================================
		/// <summary>>Returns an array containing the names of this key's values.</summary>
		/// <returns>the value names</returns>

		public string[] GetValueNames()
		{
			return this.MSregKey.GetValueNames();
		}

		//=====================================================================

		#region Type-Safe Value Accessor Methods

		//=====================================================================

		/// <summary>Sets a registry value.</summary>
        /// <param name="name">the name of the value</param>
        /// <param name="value">the value of the value</param>
		public void SetStringValue (string name, string value)
		{
			this.MSregKey.SetValue(name, value);
		}

        /// <summary>Gets a registry value.</summary>
        /// <param name="name">the name of the value</param>
		/// <returns>the value, or null if the value doesn't exist </returns>
		public string GetStringValue (string name)
		{
			return (string) this.MSregKey.GetValue(name);
		}

		/// <summary>Gets a registry value.</summary>
		/// <param name="name">the name of the value</param>
		/// <param name="defaultValue">what to return if the value doesn't exist</param>
        /// <returns>the value</returns>
		public string GetStringValue (string name, string defaultValue)
		{
			return (string) this.MSregKey.GetValue(name, defaultValue);
		}

		//=====================================================================

		/// <summary>Sets a registry value.</summary>
		/// <param name="name">the name of the value</param>
		/// <param name="value">the value of the value</param>
		public void SetIntValue (string name, int value)
		{
			this.MSregKey.SetValue(name, value);
		}

		/// <summary>Gets a registry value.</summary>
		/// <param name="name">the name of the value</param>
		/// <returns>the value</returns>
		public int GetIntValue (string name)
		{
			return (int) this.MSregKey.GetValue(name);
		}

		/// <summary>Gets a registry value.</summary>
		/// <param name="name">the name of the value</param>
		/// <param name="defaultValue">what to return if the value doesn't exist</param>
		/// <returns>the value</returns>
		public int GetIntValue (string name, int defaultValue)
		{
			return (int) this.MSregKey.GetValue(name, defaultValue);
		}

		//=====================================================================

		/// <summary>Sets a registry value to "true" or "false".</summary>
		/// <param name="name">the name of the value</param>
		/// <param name="value">the value of the value</param>
		public void SetBoolValue (string name, bool value)
		{
			this.MSregKey.SetValue(name, value ? "true" : "false");
		}

		/// <summary>Gets a registry value. 
		/// Recognizes "true", "false", "t", "f", "yes", "no", "y", and "n"; ignores case.</summary>
		/// <param name="name">the name of the value</param>
		/// <returns>the value</returns>
		public bool GetBoolValue (string name)
		{
			string s = (string) this.MSregKey.GetValue(name);
			s = s.ToLower();
			if (s == "true" || s == "t" || s == "yes" || s == "y")
				return true;
			if (s == "false" || s == "f" || s == "no" || s == "n")
				return true;
			throw new RegistryException(this.FullName, name, "bogus boolean registry value: \"" + s + "\"");
		}

		/// <summary>Gets a registry value. 
		/// Recognizes "true", "false", "t", "f", "yes", "no", "y", and "n"; ignores case.</summary>
		/// <param name="name">the name of the value</param>
		/// <param name="defaultValue">what to return if the value doesn't exist</param>
		/// <returns>the value</returns>
		public bool GetBoolValue (string name, bool defaultValue)
		{
			string s;
			try
			{
				s = (string) this.MSregKey.GetValue(name);
			}
			catch (ArgumentException)
			{
				return defaultValue;
			}

			if (s == null)
				return defaultValue;
			s = s.ToLower();
			if (s == "true" || s == "t" || s == "yes" || s == "y")
				return true;
			if (s == "false" || s == "f" || s == "no" || s == "n")
				return false;
			throw new RegistryException(this.FullName, name, "bogus boolean registry value: \"" + s + "\"");
		}

		//=====================================================================

		/// <summary>Sets a registry value.</summary>
		/// <param name="name">the name of the value</param>
		/// <param name="value">the value of the value</param>

		public void SetDecimalValue (string name, decimal value)
		{
			this.MSregKey.SetValue(name, value.ToString());
		}

		/// <summary>Gets a registry value.</summary>
		/// <param name="name">the name of the value</param>
		/// <returns>the value</returns>
		public decimal GetDecimalValue (string name)
		{
			string s = (string) this.MSregKey.GetValue(name);
			return decimal.Parse(s);
		}

		/// <summary>Gets a registry value.</summary>
		/// <param name="name">the name of the value</param>
		/// <param name="defaultValue">what to return if the value doesn't exist</param>
		/// <returns>the value</returns>
		public decimal GetDecimalValue (string name, decimal defaultValue)
		{
			try
			{
				return this.GetDecimalValue(name);
			}
			catch (ArgumentException)
			{
				return defaultValue;
			}
		}

		//=====================================================================

		/// <summary>Sets a registry value.</summary>
		/// <param name="name">the name of the value</param>
		/// <param name="value">the value of the value</param>
		public void SetEnumValue (string name, object value)
		{
			Require.Condition(value.GetType().IsEnum);
			this.MSregKey.SetValue(name, ((int) value).ToString() + " " + value.ToString());
		}

		//-------------------------------------------------------------------------
        /// <summary></summary>
        /// <param name="name"></param>
        /// <param name="enumType"></param>
        /// <returns></returns>

		public object GetEnumValue (string name, Type enumType)
		{
			Require.Condition(enumType.IsEnum);

			string s = (string) this.MSregKey.GetValue(name);
			string[] fields = s.Split(" ".ToCharArray());

			if (fields.Length == 1)
				return Enum.Parse(enumType, fields[0], true);

			if (fields.Length != 2)
				throw new RegistryException(this.FullName, name, "registry key \"" + this.MSregKey.Name +
										"\" has bogus value: \"" + s + "\"");

			object value1 = Enum.Parse(enumType, fields[0], true);
			object value2 = Enum.Parse(enumType, fields[1], true);

			if (value1 == value2)
				return value1;

			throw new RegistryException(this.FullName, name, "registry key \"" + this.MSregKey.Name +
							"\" has inconsistent value: \"" + s + "\"");
		}

		//-------------------------------------------------------------------------
        /// <summary></summary>
        /// <param name="name"></param>
        /// <param name="enumType"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>

		public object GetEnumValue (string name, Type enumType, object defaultValue)
		{
			Require.Condition(enumType.IsEnum);

			string s;

			try
			{
				s = (string) this.MSregKey.GetValue(name);
			}
			catch (ArgumentException)
			{
				return defaultValue;
			}

			if (s == null)
				return defaultValue;

			string[] fields = s.Split(" ".ToCharArray());

			if (fields.Length == 1)
				return Enum.Parse(enumType, fields[0], true);

			if (fields.Length != 2)
				throw new RegistryException(this.FullName, name, "registry key \"" + this.MSregKey.Name +
					"\" has bogus value: \"" + s + "\"");

			object value1 = Enum.Parse(enumType, fields[0], true);
			object value2 = Enum.Parse(enumType, fields[1], true);

			if (object.Equals(value1, value2))
				return value1;

			throw new RegistryException(this.FullName, name, "registry key \"" + this.MSregKey.Name +
				"\" has inconsistent value: \"" + s + "\"");
		}

		//=====================================================================

		/// <summary>Sets a registry value.</summary>
		/// <param name="name">the name of the value</param>
		/// <param name="value">the value of the value</param>
		public void SetEnumValue<T> (string name, T value) where T : struct
		{
			Require.Condition(typeof(T).IsEnum, "type must be an enum");
			this.MSregKey.SetValue(name, Convert.ToInt64(value) + " " + value.ToString());
		}

		//-------------------------------------------------------------------------
		/// <summary>
		/// Returns a registry value as an enum.  The registry value can be specified
		/// using either the enum value name or the enum value integer.
		/// </summary>
		/// <typeparam name="T">the enum type</typeparam>
		/// <param name="name">the name of the value</param>
		/// <returns>the enum value</returns>

		public T GetEnumValue<T> (string name) where T : struct
		{
			Require.Condition(typeof(T).IsEnum, "type must be an enum");

			string s = (string) this.MSregKey.GetValue(name);
			string[] fields = s.Split(" ".ToCharArray());

			if (fields.Length == 1)
				return (T) Enum.Parse(typeof(T), fields[0], true);

			if (fields.Length != 2)
				throw new RegistryException(this.FullName, name, "registry key \"" + this.MSregKey.Name +
										"\" has bogus value: \"" + s + "\"");

			object value1 = Enum.Parse(typeof(T), fields[0], true);
			object value2 = Enum.Parse(typeof(T), fields[1], true);

			if (value1 == value2)
				return (T) value1;

			throw new RegistryException(this.FullName, name, "registry key \"" + this.MSregKey.Name +
							"\" has inconsistent value: \"" + s + "\"");
		}

		//-------------------------------------------------------------------------
		/// <summary>
		/// Returns a registry value as an enum.  The registry value can be specified
		/// using either the enum value name or the enum value integer.
		/// </summary>
		/// <typeparam name="T">the enum type</typeparam>
		/// <param name="name">the name of the value</param>
		/// <param name="defaultValue">what to return if the value isn't in the registry</param>
		/// <returns>the enum value</returns>

		public T GetEnumValue<T> (string name, T defaultValue) where T : struct
		{
			Require.Condition(typeof(T).IsEnum, "type must be an enum");

			string s;

			try
			{
				s = (string) this.MSregKey.GetValue(name);
			}
			catch (ArgumentException)
			{
				return defaultValue;
			}

			if (s == null)
				return defaultValue;

			string[] fields = s.Split(" ".ToCharArray());

			if (fields.Length == 1)
				return (T) Enum.Parse(typeof(T), fields[0], true);

			if (fields.Length != 2)
				throw new RegistryException(this.FullName, name, "registry key \"" + this.MSregKey.Name +
					"\" has bogus value: \"" + s + "\"");

			object value1 = Enum.Parse(typeof(T), fields[0], true);
			object value2 = Enum.Parse(typeof(T), fields[1], true);

			if (object.Equals(value1, value2))
				return (T) value1;

			throw new RegistryException(this.FullName, name, "registry key \"" + this.MSregKey.Name +
				"\" has inconsistent value: \"" + s + "\"");
		}

		//=====================================================================

		#endregion
	}

	//#################################################################################
	/// <summary>
	/// An exception that occurred while trying to access the Windows Registry.
	/// </summary>

	public class RegistryException : Exception
	{
		/// <summary>
		/// The registry path where the error occurred.
		/// </summary>
		public readonly string Path;

		/// <summary>
		/// The name of the registry value where the error occurred, or null.
		/// </summary>
		public readonly string ValueName;

		/// <summary>
		/// constructor
		/// </summary>
		/// <param name="path">the registry path where the error occurred</param>
		/// <param name="valueName">the name of the registry value where the error occurred</param>
		/// <param name="descr">a description of the error</param>
		public RegistryException (string path, string valueName, string descr)
			: base(descr)
		{
			this.Data["Path"] = this.Path = path;
			this.Data["ValueName"] = this.ValueName = valueName;
		}

		/// <summary>
		/// constructor
		/// </summary>
		/// <param name="path">the registry path where the error occurred</param>
		/// <param name="valueName">the name of the registry value where the error occurred</param>
		/// <param name="descr">a description of the error</param>
		/// <param name="inner">the inner exception</param>
		public RegistryException (string path, string valueName, string descr, Exception inner)
			: base(descr, inner)
		{
			this.Data["Path"] = this.Path = path;
			this.Data["ValueName"] = this.ValueName = valueName;
		}

		/// required for serialization
		protected RegistryException (System.Runtime.Serialization.SerializationInfo info,
							System.Runtime.Serialization.StreamingContext context)
			: base(info, context) { }
	}
}
